﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Practices.Unity;
using Refract.Views;
using Microsoft.Practices.Composite.Regions;
using Rhino.Mocks;
using Microsoft.Practices.Composite.Presentation.Regions;
using System.Collections.ObjectModel;

namespace Refract.Tests
{   

    [TestClass]
    public class ViewModelFixture : ViewModelFixtureBase<TestViewModel> { }
    
    [TestClass]
    public class ViewModelFixtureGeneric : ViewModelFixtureBase<TestViewModelGeneric>{}

    public class ViewModelFixtureBase<TViewModel> where TViewModel : ViewModel
    {
        private const string RegionName = "testRegion";
        public const string ViewName = "View";
        private IUnityContainer _container;        
        private IRegionManager _RegionManager;
        private MockRepository _mock;

        TestViewModel _viewModel;
        
        [TestMethod]
        public void ViewModel_is_constructed()
        {
            Assert.IsNotNull(_viewModel);
        }

        [TestMethod]
        public void ViewModel_initializes_views_properly()
        {
            Assert.IsNotNull(_viewModel.Views);
        }
        [TestMethod]
        public void ViewModel_show_creates_view_and_adds_it_to_the_specified_region()
        {
            IView newView = CreateView();
            Expect.Call(delegate{newView.Show();});

            _mock.ReplayAll();
            
            _viewModel.Show(RegionName);

            Assert.AreEqual(newView, _viewModel.Views[ViewName]);
            Assert.AreEqual(RegionName, newView.RegionName);
            Assert.IsTrue(_RegionManager.Regions[RegionName].Views.Contains(newView));
            Assert.AreEqual(ViewName, newView.ViewName);
            _mock.VerifyAll();
        }

        [TestMethod]
        public void ViewModel_creates_the_correct_view()
        {
            IView newView = CreateView();
            Expect.Call(delegate { newView.Show(); });

            _container.RegisterInstance<IView>(newView);

            _mock.ReplayAll();

            IView returnedView = _viewModel.CreateView<IView>(ViewName);
            
            Assert.AreEqual(newView, returnedView);
            Assert.AreEqual(newView, _viewModel.Views[ViewName]);
            Assert.AreEqual(ViewName, newView.ViewName);
            _mock.VerifyAll();
        }
        [TestMethod]
        public void ViewModel_properly_closes_a_view_in_a_region()
        {
            IView newView = CreateView();
            Expect.Call(delegate{newView.Show();});
            Expect.Call(delegate{newView.Hide();});

            _mock.ReplayAll();

            _viewModel.Show(RegionName);
            _viewModel.Close();

            Assert.AreEqual(0, _viewModel.Views.Count, "View was not removed from views list in viewmodel");
            Assert.IsFalse(_RegionManager.Regions[RegionName].Views.Contains(newView), "View was not removed from region");
            _mock.VerifyAll();
           
        }

        [TestMethod]
        public void ViewModel_properly_closes_a_view_not_in_a_region()
        {
            IView newView = CreateView();
            Expect.Call(delegate { newView.Show(); });
            Expect.Call(delegate { newView.Hide(); });

            _mock.ReplayAll();

            _viewModel.CreateView<IView>(ViewName);
            _viewModel.Close();

            Assert.AreEqual(0, _viewModel.Views.Count, "View was not removed from views list in viewmodel");
            Assert.IsFalse(_RegionManager.Regions[RegionName].Views.Contains(newView), "View was not removed from region");
            _mock.VerifyAll();

        }

        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void ViewModel_throws_exception_when_attempting_to_close_a_view_that_was_never_opened()
        {
            IView newView = CreateView();
            Expect.Call(delegate { newView.Show(); });
            Expect.Call(delegate { newView.Hide(); });

            _viewModel.Close();

        }

        [TestInitialize]
        public void SetupTest()
        {
             _mock = new MockRepository();           

            _RegionManager = new RegionManager();
            _RegionManager.Regions.Add(new Region() { Name = RegionName });
            _container = new UnityContainer();
            _container.RegisterInstance<IRegionManager>(_RegionManager);            
            _viewModel = _container.BuildUp(new TestViewModel());

        }

        private IView CreateView()
        {
            IView newView = _mock.StrictMock<IView>();
            SetupResult.For(newView.RegionName).PropertyBehavior();
            SetupResult.For(newView.ViewName).PropertyBehavior();
            SetupResult.For(newView.ViewModel).PropertyBehavior();
            _container.RegisterInstance<IView>(newView);
            return newView;
        }
    }
}